package com.gitee.feizns.quickstart.web.restful;

import com.gitee.feizns.dynamic.Objs;
import com.gitee.feizns.dynamic.Strings;
import com.gitee.feizns.dynamic.bean.Prop;
import com.gitee.feizns.dynamic.io.Ios;
import com.gitee.feizns.dynamic.reflect.Constructors;
import com.gitee.feizns.quickstart.domain.page.PageAo;
import com.gitee.feizns.quickstart.domain.page.PageRecordsVo;
import com.gitee.feizns.quickstart.jackson.Val;
import com.gitee.feizns.quickstart.validation.ValidationUtils;
import com.gitee.feizns.quickstart.web.WebUtils;
import com.gitee.feizns.quickstart.web.ex.BadRequestException;
import com.gitee.feizns.quickstart.web.restful.annotation.RestType;
import com.gitee.feizns.quickstart.web.restful.ex.ResourceNotFoundException;
import com.gitee.feizns.quickstart.web.restful.listener.RestListener;
import com.gitee.feizns.quickstart.web.restful.validation.PatchById;
import com.gitee.feizns.quickstart.web.restful.validation.Put;
import lombok.Getter;
import lombok.SneakyThrows;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.HttpMethod;
import org.springframework.lang.NonNull;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * rest ful 工具类
 * @author feizns
 * @since 2024/10/21
 */
public class Rests implements ApplicationContextAware {

    /**
     * 执行者
     */
    @Getter
    private static RestExecutor executor;

    /**
     * 执行者
     * @return {@link RestExecutor}
     */
    public static RestExecutor execute() {
        return executor;
    }

    /**
     * 验证是否存在编号
     * @param recordType 记录类型
     * @param id 编号
     * @throws ResourceNotFoundException 不存在抛出
     */
    public static boolean existsId(Class<?> recordType, Serializable id) {
        return execute().existsId(recordType, id);
    }

    /**
     * 验证是否存在编号
     * @param recordType 记录类型
     * @param id 编号
     * @throws ResourceNotFoundException 不存在抛出
     */
    public static void ifNotExistsIdThrow(Class<?> recordType, Serializable id) {
        execute().ifNotExistsIdThrow(recordType, id);
    }

    /**
     * 创建资源
     * @param recordClass 记录类型
     * @param ao 新增数据
     * @return {@link T}
     */
    public static <T> T create(Class<T> recordClass, Object ao) {
        return CollectionUtils.firstElement(create(recordClass, Objs.toArray(ao)));
    }

    /**
     * 创建资源
     * @param recordType 记录类型
     * @param aos 新增数据
     * @return {@link T}
     */
    public static <T> List<T> create(Class<T> recordType, Object... aos) {
        return execute().create(recordType, aos);
    }

    /**
     * 更新通过编号
     * @param record 新增或更新的数据
     * @param id 编号
     * @return {@link T}
     */
    public static <T> T putById(T record, Serializable id) {
        return execute().putById(record, id);
    }

    /**
     * 更新通过编号
     * @param id 编号
     * @param recordType 记录类型
     * @param ao 请求参数
     * @return {@link T}
     */
    public static <T> T putById(Class<T> recordType, Object ao, Serializable id) {
        return execute().putById(recordType, ao, id);
    }

    /**
     * 批量新增或更新数据
     * @param recordType 待更新的数据
     * @param aos 查询请求
     * @return {@link T}
     */
    public static <T> List<T> put(Class<T> recordType, Object... aos) {
        return execute().put(recordType, aos);
    }

    /**
     * 更新通过编号
     * @param record 待更新的数据
     * @param id 编号
     * @return {@link T}
     */
    public static <T> T patchById(T record, Serializable id) {
        return execute().patchById(record, id);
    }

    /**
     * 批量更新数据
     * @param record 待更新的数据
     * @param ao 查询请求参数
     * @return {@link T}
     */
    public static <T> long patch(T record, Object ao) {
        return execute().patch(record, ao);
    }

    /**
     * 根据编号删除数据
     * @param recordType 记录类型
     * @param id 编号
     * @return {@link T}
     */
    public static <T> T deleteById(Class<T> recordType, Serializable id) {
        return execute().deleteById(recordType, id);
    }

    /**
     * 批量删除返回删除的行数
     * @param recordType 记录类型
     * @param ao 查询参数列表
     * @return long
     */
    public static <T> long delete(Class<T> recordType, Object ao) {
        return execute().delete(recordType, ao);
    }

    /**
     * 通过编号获取数据
     * @param recordType 记录类型
     * @param id 编号
     * @return {@link T}
     */
    public static <T> T getById(Class<T> recordType, Serializable id) {
        return execute().getById(recordType, id);
    }

    /**
     * 查询分页参数
     * @param recordType 记录类型
     * @param page 分页参数
     * @param aos 参数列表
     * @return {@link PageRecordsVo}<{@link T}>
     */
    public static <T> PageRecordsVo<T> page(Class<T> recordType, PageAo page, Object... aos) {
        return execute().page(recordType, page, aos);
    }

    /**
     * 批量 CRUD
     * @param recordClass record 类
     * @return {@link Object }
     */
    @SneakyThrows
    public static <T> Object crud(Class<T> recordClass) {
        HttpServletRequest req = WebUtils.getReq();
        HttpMethod method = HttpMethod.valueOf(req.getMethod());
        if ( method == HttpMethod.GET )
            return page(recordClass, WebUtils.requestParamsBindTo(PageAo.class), req.getParameterMap());
        else if ( method == HttpMethod.POST ) {
            //获取请求体
            List<T> records = requestBody().asList(recordClass).orElse(new ArrayList<>());
            //清除id并校验请求体
            records.forEach(Rests::clearId);
            //创建
            List<T> ret = create(recordClass, records.toArray());
            return ret != null && ret.size() == 1 ? ret.get(0) : ret;
        } else if ( method == HttpMethod.PUT ) {
            //获取请求体
            List<T> records = requestBody().asList(recordClass).orElse(new ArrayList<>());
            //新增或更新
            return put(recordClass, records.toArray());
        } else if ( method == HttpMethod.PATCH ) {
            //获取请求体
            T record = requestBody().as(recordClass).orElseThrow(() -> new BadRequestException("请求体不能为空."));
            //清除id
            clearId(record);
            //更新
            return patch(record, req.getParameterMap());
        } else if ( method == HttpMethod.DELETE )
            return execute().delete(recordClass, req.getParameterMap());
        throw new UnsupportedOperationException("不支持的请求方法:" + method);
    }

    /**
     * 根据WEB编号获取、更新、删除
     * @param recordClass record 类
     * @param id 编号
     * @return {@link T}
     */
    @SneakyThrows
    public static <T> T crudById(Class<T> recordClass, Serializable id) {
        HttpServletRequest req = WebUtils.getReq();
        HttpMethod method = HttpMethod.valueOf(req.getMethod());
        if ( method == HttpMethod.GET )
            return getById(recordClass, id);
        else if ( method == HttpMethod.POST ) {
            //获取请求体
            T record = requestBody().as(recordClass).orElseThrow(() -> new BadRequestException("请求体不能为空."));
            //清除id
            Rests.clearId(record);
            //创建
            return create(recordClass, record);
        } else if ( method == HttpMethod.PUT ) {
            //获取请求体
            T record = requestBody().as(recordClass).orElseThrow(() -> new BadRequestException("请求体不能为空."));
            //校验请求体
            ValidationUtils.validateIfFailsThenThrow(record, Put.class);
            //更新
            return putById(record, id);
        } else if ( method == HttpMethod.PATCH ) {
            //获取请求体
            T record = requestBody().as(recordClass).orElseThrow(() -> new BadRequestException("请求体不能为空."));
            //清除id
            clearId(record);
            //校验请求体
            ValidationUtils.validateIfFailsThenThrow(record, PatchById.class);
            //更新
            return patchById(record, id);
        } else if ( method == HttpMethod.DELETE )
            return deleteById(recordClass, id);
        throw new BadRequestException(String.format("Request method '%s' not supported", method));
    }

    /**
     * 名字
     * @param recordType 记录类型
     * @return {@link String}
     */
    public static String name(Class<?> recordType) {
        RestType annotation = recordType.getAnnotation(RestType.class);
        String name = recordType.getSimpleName();
        if ( annotation != null && Strings.hasText(annotation.name()) )
            name = annotation.name();
        return name;
    }

    /**
     * 获取编号属性名字
     * @param obj 对象
     * @return {@link String}
     */
    public static String id(Object obj) {
        RestType annotation = obj.getClass().getAnnotation(RestType.class);
        return annotation != null ? annotation.id() : "id";
    }

    /**
     * 获取rest类型
     * @param recordClass record 类
     * @return {@link Class}<{@link ?}>
     */
    public static Class<?> type(Class<?> recordClass) {
        RestType annotation = recordClass.getAnnotation(RestType.class);
        if ( annotation != null ) {
            if ( annotation.value() != RestType.Entity.class )
                return annotation.value();
        }
        return recordClass;
    }

    /**
     * REST API 侦听器
     * @param recordClass record 类
     * @return {@link RestListener}<{@link T}>
     */
    @SuppressWarnings("unchecked")
    public static <T> RestListener<T> restListener(Class<T> recordClass) {
        return RestListener.class.isAssignableFrom(recordClass) ? (RestListener<T>) Constructors.newInstance(recordClass) : null;
    }

    /**
     * 获取编号
     * @param obj 对象
     * @return {@link Object}
     */
    public static Serializable getId(Object obj) {
        return (Serializable) Prop.of(obj, id(obj)).val();
    }

    /**
     * 设置编号
     * @param obj 对象
     * @param id 编号
     */
    public static void setId(Object obj, Object id) {
        Prop.of(obj, id(obj)).set(id);
    }

    /**
     * 清除编号
     * @param obj 对象
     */
    public static void clearId(Object obj) {
        setId(obj, null);
    }

    /**
     * 请求正文
     * @return {@link Val}
     */
    @SneakyThrows
    public static Val requestBody() {
        Val body = Val.parse(Ios.asString(WebUtils.getReq().getInputStream()));
        if ( body.isNull() )
            throw new BadRequestException("Required request body is missing.");
        return body;
    }

    /**
     * 设置应用程序上下文
     * @param applicationContext 应用程序上下文
     * @throws BeansException bean 异常
     */
    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        Rests.executor = applicationContext.getBean(RestExecutor.class);
    }

}
